home *** CD-ROM | disk | FTP | other *** search
/ Collection of Internet / Collection of Internet.iso / msdos / lynx / source / doslynx / src / htfile.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-10-25  |  29.2 KB  |  1,138 lines

  1. /*            File Access                HTFile.c
  2. **            ===========
  3. **
  4. **    This is unix-specific code in general, with some VMS bits.
  5. **    These are routines for file access used by browsers.
  6. **
  7. ** History:
  8. **       Feb 91    Written Tim Berners-Lee CERN/CN
  9. **       Apr 91    vms-vms access included using DECnet syntax
  10. **    26 Jun 92 (JFG) When running over DECnet, suppressed FTP.
  11. **            Fixed access bug for relative names on VMS.
  12. **       Sep 93 (MD)  Access to VMS files allows sharing.
  13. **    15 Nov 93 (MD)    Moved HTVMSname to HTVMSUTILS.C
  14. **
  15. ** Bugs:
  16. **    FTP: Cannot access VMS files from a unix machine.
  17. **      How can we know that the
  18. **    target machine runs VMS?
  19. */
  20.  
  21. #include"capalloc.h"
  22. #include "HTFile.h"        /* Implemented here */
  23. #include"capstdio.h"
  24.  
  25.  
  26. #define INFINITY 512        /* file name length @@ FIXME */
  27. #define MULTI_SUFFIX ".multi"   /* Extension for scanning formats */
  28.  
  29. #include "HTUtils.h"
  30.  
  31. #ifdef VMS
  32. #include "HTVMSUtils.h"
  33. #endif /* VMS */
  34.  
  35. #include "HTParse.h"
  36. #include "tcp.h"
  37. #include "HTTCP.h"
  38. #ifndef DECNET
  39. #include "HTFTP.h"
  40. #endif
  41. #include "HTAnchor.h"
  42. #include "HTAtom.h"
  43. #include "HTWriter.h"
  44. #include "HTFWriter.h"
  45. #include "HTInit.h"
  46. #include "HTBTree.h"
  47.  
  48. typedef struct _HTSuffix {
  49.     char *        suffix;
  50.     HTAtom *    rep;
  51.     HTAtom *    encoding;
  52.     float        quality;
  53. } HTSuffix;
  54.  
  55.  
  56. #ifdef USE_DIRENT        /* Set this for Sys V systems */
  57. #define STRUCT_DIRENT struct dirent
  58. #else
  59. #define STRUCT_DIRENT struct direct
  60. #endif
  61.  
  62. #include "HTML.h"        /* For directory object building */
  63.  
  64. #define PUTC(c) (*target->isa->put_character)(target, c)
  65. #define PUTS(s) (*target->isa->put_string)(target, s)
  66. #define START(e) (*target->isa->start_element)(target, e, 0, 0)
  67. #define END(e) (*target->isa->end_element)(target, e)
  68. #define FREE_TARGET (*target->isa->free)(target)
  69. struct _HTStructured {
  70.     CONST HTStructuredClass *    isa;
  71.     /* ... */
  72. };
  73.  
  74.  
  75. /*                   Controlling globals
  76. **
  77. */
  78.  
  79. PUBLIC int HTDirAccess = HT_DIR_OK;
  80. PUBLIC int HTDirReadme = HT_DIR_README_TOP;
  81.  
  82. PRIVATE char *HTMountRoot = "/Net/";        /* Where to find mounts */
  83. #ifdef VMS
  84. PRIVATE char *HTCacheRoot = "/WWW$SCRATCH";   /* Where to cache things */
  85. #else
  86. PRIVATE char *HTCacheRoot = "/tmp/W3_Cache_";   /* Where to cache things */
  87. #endif
  88.  
  89. /* PRIVATE char *HTSaveRoot  = "$(HOME)/WWW/";*/    /* Where to save things */
  90.  
  91.  
  92. /*    Suffix registration
  93. */
  94.  
  95. PRIVATE HTList * HTSuffixes = 0;
  96. PRIVATE HTSuffix no_suffix = { "*", NULL, NULL, 1.0 };
  97. PRIVATE HTSuffix unknown_suffix = { "*.*", NULL, NULL, 1.0};
  98.  
  99.  
  100. /*    Define the representation associated with a file suffix
  101. **    -------------------------------------------------------
  102. **
  103. **    Calling this with suffix set to "*" will set the default
  104. **    representation.
  105. **    Calling this with suffix set to "*.*" will set the default
  106. **    representation for unknown suffix files which contain a ".".
  107. **
  108. **    If filename suffix is already defined its previous
  109. **    definition is overridden.
  110. */
  111. PUBLIC void HTSetSuffix ARGS4(
  112.     CONST char *,    suffix,
  113.     CONST char *,    representation,
  114.     CONST char *,    encoding,
  115.     float,        value)
  116. {
  117.     
  118.     HTSuffix * suff;
  119.  
  120.     if (strcmp(suffix, "*")==0) suff = &no_suffix;
  121.     else if (strcmp(suffix, "*.*")==0) suff = &unknown_suffix;
  122.     else {
  123.     HTList *cur = HTSuffixes;
  124.  
  125.     while (NULL != (suff = (HTSuffix*)HTList_nextObject(cur))) {
  126.         if (suff->suffix && 0==strcmp(suff->suffix, suffix))
  127.         break;
  128.     }
  129.     if (!suff) { /* Not found -- create a new node */
  130.         suff = (HTSuffix*) calloc(1, sizeof(HTSuffix));
  131.         if (suff == NULL) outofmem(__FILE__, "HTSetSuffix");
  132.     
  133.         if (!HTSuffixes) HTSuffixes = HTList_new();
  134.         HTList_addObject(HTSuffixes, suff);
  135.     
  136.         StrAllocCopy(suff->suffix, suffix);
  137.     }
  138.     }
  139.  
  140.     suff->rep = HTAtom_for(representation);
  141.  
  142.     {
  143.         char * enc = NULL;
  144.     char * p;
  145.     StrAllocCopy(enc, encoding);
  146.     for (p=enc; *p; p++) *p = TOLOWER(*p);
  147.     suff->encoding = HTAtom_for(encoding);
  148.     }
  149.     
  150.     suff->quality = value;
  151. }
  152.  
  153.  
  154.  
  155.  
  156.  
  157.  
  158.  
  159. /*    Send README file
  160. **
  161. **  If a README file exists, then it is inserted into the document here.
  162. */
  163.  
  164. #ifdef GOT_READ_DIR
  165. PRIVATE void do_readme ARGS2(HTStructured *, target, CONST char *, localname)
  166.     FILE * fp;
  167.     char * readme_file_name = 
  168.     malloc(strlen(localname)+ 1 + strlen(HT_DIR_README_FILE) + 1);
  169.     strcpy(readme_file_name, localname);
  170.     strcat(readme_file_name, "/");
  171.     strcat(readme_file_name, HT_DIR_README_FILE);
  172.     
  173.     fp = fopen(readme_file_name,  "r");
  174.     
  175.     if (fp) {
  176.     HTStructuredClass targetClass;
  177.     
  178.     targetClass =  *target->isa;    /* (Can't init agregate in K&R) */
  179. #ifdef MSDOSMEM
  180.     {
  181.         extern void *vp_msdosmem;
  182.         extern void **vpp_msdosmem;
  183.         vp_msdosmem = NULL;
  184.         vpp_msdosmem = NULL;
  185.     }
  186. #endif /* MSDOSMEM */
  187.     START(HTML_PRE);
  188.     for(;;){
  189.         char c = fgetc(fp);
  190.         if (c == (char)EOF) break;
  191.         switch (c) {
  192.             case '&':
  193.         case '<':
  194.         case '>':
  195.             PUTC('&');
  196.             PUTC('#');
  197.             PUTC((char)(c / 10));
  198.             PUTC((char) (c % 10));
  199.             PUTC(';');
  200.             break;
  201. /*            case '\n':
  202.             PUTC('\r');    
  203. Bug removed thanks to joe@athena.mit.edu */            
  204.         default:
  205.             PUTC(c);
  206.         }
  207.     }
  208.     END(HTML_PRE);
  209.     fclose(fp);
  210.     } 
  211. }
  212. #endif
  213.  
  214.  
  215. /*    Make the cache file name for a W3 document
  216. **    ------------------------------------------
  217. **    Make up a suitable name for saving the node in
  218. **
  219. **    E.g.    /tmp/WWW_Cache_news/1234@cernvax.cern.ch
  220. **        /tmp/WWW_Cache_http/crnvmc/FIND/xx.xxx.xx
  221. **
  222. ** On exit,
  223. **    returns    a malloc'ed string which must be freed by the caller.
  224. */
  225. PUBLIC char * HTCacheFileName ARGS1(CONST char *,name)
  226. {
  227.     char * access = HTParse(name, "", PARSE_ACCESS);
  228.     char * host = HTParse(name, "", PARSE_HOST);
  229.     char * path = HTParse(name, "", PARSE_PATH+PARSE_PUNCTUATION);
  230.     
  231.     char * result;
  232.     result = (char *)malloc(
  233.         strlen(HTCacheRoot)+strlen(access)
  234.         +strlen(host)+strlen(path)+6+1);
  235.     if (result == NULL) outofmem(__FILE__, "HTCacheFileName");
  236.     sprintf(result, "%s/WWW/%s/%s%s", HTCacheRoot, access, host, path);
  237.     free(path);
  238.     free(access);
  239.     free(host);
  240.     return result;
  241. }
  242.  
  243.  
  244. /*    Open a file for write, creating the path
  245. **    ----------------------------------------
  246. */
  247. #ifdef NOT_IMPLEMENTED
  248. PRIVATE int HTCreatePath ARGS1(CONST char *,path)
  249. {
  250.     return -1;
  251. }
  252. #endif
  253.  
  254. /*    Convert filenames between local and WWW formats
  255. **    -----------------------------------------------
  256. **    Make up a suitable name for saving the node in
  257. **
  258. **    E.g.    $(HOME)/WWW/news/1234@cernvax.cern.ch
  259. **        $(HOME)/WWW/http/crnvmc/FIND/xx.xxx.xx
  260. **
  261. ** On exit,
  262. **    returns    a malloc'ed string which must be freed by the caller.
  263. */
  264. PUBLIC char * HTLocalName ARGS1(CONST char *,name)
  265. {
  266.     char * access = HTParse(name, "", PARSE_ACCESS);
  267.     char * host = HTParse(name, "", PARSE_HOST);
  268.     char * path = HTParse(name, "", PARSE_PATH+PARSE_PUNCTUATION);
  269.     
  270.     HTUnEscape(path);    /* Interpret % signs */
  271.     
  272.     if (0==strcmp(access, "file")) { /* local file */
  273.         free(access);    
  274.     if ((0==strcasecomp(host, HTHostName())) ||
  275.         (0==strcasecomp(host, "localhost")) || !*host) {
  276.         free(host);
  277. #ifndef RELEASE
  278.         if (TRACE) fprintf(stderr, "Node `%s' means path `%s'\n", name, path);
  279. #endif /* RELEASE */
  280. #ifdef MSDOS
  281.         {
  282.             /*
  283.              *    Convert the path to a dos file name.
  284.              */
  285.             extern void urltodos(char *);
  286.  
  287.             urltodos(path);
  288.         }
  289. #endif /* MSDOS */
  290.         return(path);
  291.     } else {
  292.         char * result = (char *)malloc(
  293.                     strlen("/Net/")+strlen(host)+strlen(path)+1);
  294.           if (result == NULL) outofmem(__FILE__, "HTLocalName");
  295.         sprintf(result, "%s%s%s", "/Net/", host, path);
  296.         free(host);
  297.         free(path);
  298. #ifndef RELEASE
  299.         if (TRACE) fprintf(stderr, "Node `%s' means file `%s'\n", name, result);
  300. #endif /* RELEASE */
  301.         return result;
  302.     }
  303.     } else {  /* other access */
  304.     char * result;
  305.         CONST char * home =  (CONST char*)getenv("HOME");
  306. #ifdef VMS
  307.     if (!home) 
  308.         home = HTCacheRoot; 
  309.     else
  310.            home = HTVMS_wwwName(home);
  311. #else /* not VMS */
  312.     if (!home) home = "/tmp"; 
  313. #endif /* not VMS */
  314.     result = (char *)malloc(
  315.         strlen(home)+strlen(access)+strlen(host)+strlen(path)+6+1);
  316.       if (result == NULL) outofmem(__FILE__, "HTLocalName");
  317.     sprintf(result, "%s/WWW/%s/%s%s", home, access, host, path);
  318.     free(path);
  319.     free(access);
  320.     free(host);
  321.     return result;
  322.     }
  323. }
  324.  
  325.  
  326. /*    Make a WWW name from a full local path name
  327. **
  328. ** Bugs:
  329. **    At present, only the names of two network root nodes are hand-coded
  330. **    in and valid for the NeXT only. This should be configurable in
  331. **    the general case.
  332. */
  333.  
  334. PUBLIC char * WWW_nameOfFile ARGS1 (CONST char *,name)
  335. {
  336.     char * result;
  337. #ifdef NeXT
  338.     if (0==strncmp("/private/Net/", name, 13)) {
  339.     result = (char *)malloc(7+strlen(name+13)+1);
  340.     if (result == NULL) outofmem(__FILE__, "WWW_nameOfFile");
  341.     sprintf(result, "file://%s", name+13);
  342.     } else
  343. #endif
  344.     if (0==strncmp(HTMountRoot, name, 5)) {
  345.     result = (char *)malloc(7+strlen(name+5)+1);
  346.     if (result == NULL) outofmem(__FILE__, "WWW_nameOfFile");
  347.     sprintf(result, "file://%s", name+5);
  348.     } else {
  349.         result = (char *)malloc(7+strlen(HTHostName())+strlen(name)+1);
  350.     if (result == NULL) outofmem(__FILE__, "WWW_nameOfFile");
  351.     sprintf(result, "file://%s%s", HTHostName(), name);
  352.     }
  353. #ifndef RELEASE
  354.     if (TRACE) fprintf(stderr, "File `%s'\n\tmeans node `%s'\n", name, result);
  355. #endif /* RELEASE */
  356.     return result;
  357. }
  358.  
  359.  
  360. /*    Determine a suitable suffix, given the representation
  361. **    -----------------------------------------------------
  362. **
  363. ** On entry,
  364. **    rep    is the atomized MIME style representation
  365. **
  366. ** On exit,
  367. **    returns    a pointer to a suitable suffix string if one has been
  368. **        found, else "".
  369. */
  370. PUBLIC CONST char * HTFileSuffix ARGS1(HTAtom*, rep)
  371. {
  372.     HTSuffix * suff;
  373.     int n;
  374.     int i;
  375.  
  376. #ifndef NO_INIT    
  377.     if (!HTSuffixes) HTFileInit();
  378. #endif
  379.     n = HTList_count(HTSuffixes);
  380.  
  381.     for(i=0; i<n; i++) {
  382.     suff = HTList_objectAt(HTSuffixes, i);
  383.  
  384.     if (suff->rep == rep) {
  385.         return((const char *)(suff->suffix));    /* OK -- found */
  386.     }
  387.     }
  388.  
  389.     return "";        /* Dunno */
  390. }
  391.  
  392.  
  393. /*    Determine file format from file name
  394. **    ------------------------------------
  395. **
  396. **    This version will return the representation and also set
  397. **    a variable for the encoding.
  398. **
  399. **    It will handle for example  x.txt, x.txt,Z, x.Z
  400. */
  401.  
  402. PUBLIC HTFormat HTFileFormat ARGS2 (
  403.             CONST char *,    filename,
  404.             HTAtom **,    pencoding)
  405.  
  406. {
  407.     HTSuffix * suff;
  408.     int n;
  409.     int i;
  410.     int lf = strlen(filename);
  411.  
  412. #ifndef NO_INIT    
  413.     if (!HTSuffixes) HTFileInit();
  414. #endif
  415.     *pencoding = NULL;
  416.     n = HTList_count(HTSuffixes);
  417.     for(i=0; i<n; i++) {
  418.         int ls;
  419.     suff = HTList_objectAt(HTSuffixes, i);
  420.     ls = strlen(suff->suffix);
  421.     if ((ls <= lf) && 0==strcmp(suff->suffix, filename + lf - ls)) {
  422.         int j;
  423.         *pencoding = suff->encoding;
  424.         if (suff->rep) return suff->rep;        /* OK -- found */
  425.         
  426.         for(j=0; j<n; j++) {  /* Got encoding, need representation */
  427.         int ls2;
  428.         suff = HTList_objectAt(HTSuffixes, j);
  429.         ls2 = strlen(suff->suffix);
  430.         if ((ls <= lf) && 0==strncmp(
  431.             suff->suffix, filename + lf - ls -ls2, ls2)) {
  432.             if (suff->rep) return suff->rep;
  433.         }
  434.         }
  435.         
  436.     }
  437.     }
  438.     
  439.     /* defaults tree */
  440.     
  441.     suff = strchr(filename, '.') ?     /* Unknown suffix */
  442.      ( unknown_suffix.rep ? &unknown_suffix : &no_suffix)
  443.      : &no_suffix;
  444.      
  445.     /* set default encoding unless found with suffix already */
  446.     if (!*pencoding) *pencoding = suff->encoding ? suff->encoding
  447.                     : HTAtom_for("binary");
  448.     return suff->rep ? suff->rep : WWW_BINARY;
  449. }
  450.  
  451.  
  452. /*    Determine value from file name
  453. **    ------------------------------
  454. **
  455. */
  456.  
  457. PUBLIC float HTFileValue ARGS1 (CONST char *,filename)
  458.  
  459. {
  460.     HTSuffix * suff;
  461.     int n;
  462.     int i;
  463.     int lf = strlen(filename);
  464.  
  465. #ifndef NO_INIT    
  466.     if (!HTSuffixes) HTFileInit();
  467. #endif
  468.     n = HTList_count(HTSuffixes);
  469.     for(i=0; i<n; i++) {
  470.         int ls;
  471.     suff = HTList_objectAt(HTSuffixes, i);
  472.     ls = strlen(suff->suffix);
  473.     if ((ls <= lf) && 0==strcmp(suff->suffix, filename + lf - ls)) {
  474. #ifndef RELEASE
  475.         if (TRACE) fprintf(stderr, "File: Value of %s is %.3f\n",
  476.                    filename, suff->quality);
  477. #endif /* RELEASE */
  478.         return suff->quality;        /* OK -- found */
  479.     }
  480.     }
  481.     return 0.3;        /* Dunno! */
  482. }
  483.  
  484.  
  485. /*    Determine write access to a file
  486. **    --------------------------------
  487. **
  488. ** On exit,
  489. **    return value    YES if file can be accessed and can be written to.
  490. **
  491. ** Bugs:
  492. **    1.    No code for non-unix systems.
  493. **    2.    Isn't there a quicker way?
  494. */
  495.  
  496. #ifdef VMS
  497. #define NO_GROUPS
  498. #endif
  499. #ifdef NO_UNIX_IO
  500. #define NO_GROUPS
  501. #endif
  502. #ifdef PCNFS
  503. #define NO_GROUPS
  504. #endif
  505. #ifdef MSDOS
  506. #define NO_GROUPS
  507. #endif
  508.  
  509. PUBLIC BOOL HTEditable ARGS1 (CONST char *,filename)
  510. {
  511. #ifdef NO_GROUPS
  512.     return NO;        /* Safe answer till we find the correct algorithm */
  513. #else
  514.     int     groups[NGROUPS];
  515.     uid_t    myUid;
  516.     int        ngroups;            /* The number of groups  */
  517.     struct stat    fileStatus;
  518.     int        i;
  519.  
  520.     if (stat(filename, &fileStatus))        /* Get details of filename */
  521.     return NO;                /* Can't even access file! */
  522.  
  523.     ngroups = getgroups(NGROUPS, groups);    /* Groups to which I belong  */
  524.     myUid = geteuid();                /* Get my user identifier */
  525.  
  526. #ifndef RELEASE
  527.     if (TRACE) {
  528.     int i;
  529.     fprintf(stderr,
  530.         "File mode is 0%o, uid=%d, gid=%d. My uid=%d, %d groups (",
  531.         (unsigned int) fileStatus.st_mode, fileStatus.st_uid,
  532.         fileStatus.st_gid,
  533.         myUid, ngroups);
  534.     for (i=0; i<ngroups; i++) fprintf(stderr, " %d", groups[i]);
  535.     fprintf(stderr, ")\n");
  536.     }
  537. #endif /* RELEASE */
  538.     
  539.     if (fileStatus.st_mode & 0002)        /* I can write anyway? */
  540.         return YES;
  541.     
  542.     if ((fileStatus.st_mode & 0200)        /* I can write my own file? */
  543.      && (fileStatus.st_uid == myUid))
  544.         return YES;
  545.  
  546.     if (fileStatus.st_mode & 0020)        /* Group I am in can write? */
  547.     {
  548.        for (i=0; i<ngroups; i++) {
  549.             if (groups[i] == fileStatus.st_gid)
  550.         return YES;
  551.     }
  552.     }
  553. #ifndef RELEASE
  554.     if (TRACE) fprintf(stderr, "\tFile is not editable.\n");
  555. #endif /* RELEASE */
  556.     return NO;                    /* If no excuse, can't do */
  557. #endif
  558. }
  559.  
  560.  
  561. /*    Make a save stream
  562. **    ------------------
  563. **
  564. **    The stream must be used for writing back the file.
  565. **    @@@ no backup done
  566. */
  567. PUBLIC HTStream * HTFileSaveStream ARGS1(HTParentAnchor *, anchor)
  568. {
  569.  
  570.     CONST char * addr = HTAnchor_address((HTAnchor*)anchor);
  571.     char *  localname = HTLocalName(addr);
  572.  
  573.     FILE* fp = fopen(localname, "w");
  574.     if (!fp) return NULL;
  575.     
  576.     return HTFWriter_new(fp);
  577.     
  578. }
  579.  
  580. /*      Output one directory entry
  581. **
  582. */
  583. PUBLIC void HTDirEntry ARGS3(HTStructured *, target,
  584.          CONST char * , tail,
  585.          CONST char *,  entry)
  586. {
  587.     char * relative;
  588.     char * escaped = HTEscape(entry, URL_XPALPHAS);
  589.  
  590.     /* If empty tail, gives absolute ref below */
  591.     relative = (char*) malloc(
  592.                   strlen(tail) + strlen(escaped)+2);
  593.     if (relative == NULL) outofmem(__FILE__, "DirRead");
  594.     sprintf(relative, "%s/%s", tail, escaped);
  595.     HTStartAnchor(target, NULL, relative);
  596.     free(escaped);
  597.     free(relative);
  598. }
  599.  
  600. /*      Output parent directory entry
  601. **
  602. **    This gives the TITLE and H1 header, and also a link
  603. **    to the parent directory if appropriate.
  604. */
  605. PUBLIC void HTDirTitles ARGS2(HTStructured *, target,
  606.          HTAnchor * , anchor)
  607.  
  608. {
  609.     char * logical = HTAnchor_address(anchor);
  610.     char * path = HTParse(logical, "", PARSE_PATH + PARSE_PUNCTUATION);
  611.     char * current;
  612.  
  613.     current = strrchr(path, '/');    /* last part or "" */
  614.     free(logical);
  615.  
  616.     {
  617.       char * printable = NULL;
  618.       StrAllocCopy(printable, (current + 1));
  619.       HTUnEscape(printable);
  620. #ifdef MSDOSMEM
  621.     {
  622.         extern void *vp_msdosmem;
  623.         extern void **vpp_msdosmem;
  624.         vp_msdosmem = NULL;
  625.         vpp_msdosmem = NULL;
  626.     }
  627. #endif /* MSDOSMEM */
  628.       START(HTML_TITLE);
  629.       PUTS(*printable ? printable : "Welcome ");
  630.       PUTS(" directory");
  631.       END(HTML_TITLE);    
  632.     
  633. #ifdef MSDOSMEM
  634.     {
  635.         extern void *vp_msdosmem;
  636.         extern void **vpp_msdosmem;
  637.         vp_msdosmem = NULL;
  638.         vpp_msdosmem = NULL;
  639.     }
  640. #endif /* MSDOSMEM */
  641.       START(HTML_H1);
  642.       PUTS(*printable ? printable : "Welcome");
  643.       END(HTML_H1);
  644.       free(printable);
  645.     }
  646.  
  647.     /*  Make link back to parent directory
  648.      */
  649.  
  650.     if (current && current[1]) {   /* was a slash AND something else too */
  651.         char * parent;
  652.     char * relative;
  653.     *current++ = 0;
  654.       parent = strrchr(path, '/');  /* penultimate slash */
  655.  
  656.     relative = (char*) malloc(strlen(current) + 4);
  657.     if (relative == NULL) outofmem(__FILE__, "DirRead");
  658.     sprintf(relative, "%s/..", current);
  659.     HTStartAnchor(target, "", relative);
  660.     free(relative);
  661.  
  662.     PUTS("Up to ");
  663.     if (parent) {
  664.       char * printable = NULL;
  665.       StrAllocCopy(printable, parent + 1);
  666.       HTUnEscape(printable);
  667.       PUTS(printable);
  668.       free(printable);
  669.     } else {
  670.       PUTS("/");
  671.     }
  672.  
  673.     END(HTML_A);
  674.  
  675.     }
  676.     free(path);
  677. }
  678.         
  679.  
  680.  
  681. /*    Load a document
  682. **    ---------------
  683. **
  684. ** On entry,
  685. **    addr        must point to the fully qualified hypertext reference.
  686. **            This is the physsical address of the file
  687. **
  688. ** On exit,
  689. **    returns        <0        Error has occured.
  690. **            HTLOADED    OK 
  691. **
  692. */
  693. PUBLIC int HTLoadFile ARGS4 (
  694.     CONST char *,        addr,
  695.     HTParentAnchor *,    anchor,
  696.     HTFormat,        format_out,
  697.     HTStream *,        sink
  698. )
  699. {
  700.     char * filename;
  701.     HTFormat format;
  702.     char * nodename = 0;
  703.     char * newname=0;    /* Simplified name of file */
  704.     HTAtom * encoding;    /* @@ not used yet */
  705.     
  706. /*    Reduce the filename to a basic form (hopefully unique!)
  707. */
  708.     StrAllocCopy(newname, addr);
  709.     filename=HTParse(newname, "", PARSE_PATH|PARSE_PUNCTUATION);
  710.     nodename=HTParse(newname, "", PARSE_HOST);
  711.     free(newname);
  712.     
  713.     format = HTFileFormat(filename, &encoding);
  714.  
  715.  
  716. #ifdef VMS
  717. /* Assume that the file is in Unix-style syntax if it contains a '/'
  718.    after the leading one @@ */
  719.     {
  720.         FILE * fp;
  721.     char * vmsname = strchr(filename + 1, '/') ?
  722.       HTVMS_name(nodename, filename) : filename + 1;
  723.     fp = fopen(vmsname, "r", "shr=put", "shr=upd");
  724.     
  725. /*    If the file wasn't VMS syntax, then perhaps it is ultrix
  726. */
  727.     if (!fp) {
  728.         char ultrixname[INFINITY];
  729. #ifndef RELEASE
  730.         if (TRACE) fprintf(stderr, "HTFile: Can't open as %s\n", vmsname);
  731. #endif /* RELEASE */
  732.         sprintf(ultrixname, "%s::\"%s\"", nodename, filename);
  733.           fp = fopen(ultrixname, "r", "shr=put", "shr=upd");
  734.         if (!fp) {
  735. #ifndef RELEASE
  736.         if (TRACE) fprintf(stderr,
  737.                    "HTFile: Can't open as %s\n", ultrixname);
  738. #endif /* RELEASE */
  739.         }
  740.     }
  741.         if (fp)
  742.         {
  743.         if (HTEditable(vmsname)) {
  744.         HTAtom * put = HTAtom_for("PUT");
  745.         HTList * methods = HTAnchor_methods(anchor);
  746.         if (HTList_indexOf(methods, put) == (-1)) {
  747.                HTList_addObject(methods, put);
  748.             }
  749.         }
  750.         HTParseFile(format, format_out, anchor, fp, sink);
  751.         fclose(fp);
  752.             return HT_LOADED;
  753.         }  /* If successfull open */
  754.     }
  755. #else
  756.  
  757.     free(filename);
  758.     
  759. /*    For unix, we try to translate the name into the name of a transparently
  760. **    mounted file.
  761. **
  762. **    Not allowed in secure (HTClienntHost) situations TBL 921019
  763. */
  764. #ifndef NO_UNIX_IO
  765.     /*  Need protection here for telnet server but not httpd server */
  766.      
  767.     if (!HTSecure) {        /* try local file system */
  768.     char * localname = HTLocalName(addr);
  769.     struct stat dir_info;
  770.     
  771. #ifdef GOT_READ_DIR
  772.  
  773. /*              Multiformat handling
  774. **
  775. **    If needed, scan directory to find a good file.
  776. **  Bug:  we don't stat the file to find the length
  777. */
  778.     if ( (strlen(localname) > strlen(MULTI_SUFFIX))
  779.        && (0==strcmp(localname + strlen(localname) - strlen(MULTI_SUFFIX),
  780.               MULTI_SUFFIX))) {
  781.         DIR *dp;
  782.  
  783.         STRUCT_DIRENT * dirbuf;
  784.         float best = NO_VALUE_FOUND;    /* So far best is bad */
  785.         HTFormat best_rep = NULL;    /* Set when rep found */
  786.         STRUCT_DIRENT best_dirbuf;    /* Best dir entry so far */
  787.  
  788.         char * base = strrchr(localname, '/');
  789.         int baselen;
  790.  
  791.         if (!base || base == localname) goto forget_multi;
  792.         *base++ = 0;        /* Just got directory name */
  793.         baselen = strlen(base)- strlen(MULTI_SUFFIX);
  794.         base[baselen] = 0;    /* Chop off suffix */
  795.  
  796.         dp = opendir(localname);
  797.         if (!dp) {
  798. forget_multi:
  799.         free(localname);
  800.         return HTLoadError(sink, 500,
  801.             "Multiformat: directory scan failed.");
  802.         }
  803.         
  804.         while ((dirbuf = readdir(dp))!=0) {
  805.             /* while there are directory entries to be read */
  806.         if (dirbuf->d_ino == 0) continue;
  807.                 /* if the entry is not being used, skip it */
  808.         
  809.         if (dirbuf->d_namlen > baselen &&      /* Match? */
  810.             !strncmp(dirbuf->d_name, base, baselen)) {    
  811.             HTFormat rep = HTFileFormat(dirbuf->d_name, &encoding);
  812.             float value = HTStackValue(rep, format_out,
  813.                             HTFileValue(dirbuf->d_name),
  814.                         0.0  /* @@@@@@ */);
  815.             if (value != NO_VALUE_FOUND) {
  816. #ifndef RELEASE
  817.             if (TRACE) fprintf(stderr,
  818.                 "HTFile: value of presenting %s is %f\n",
  819.                 HTAtom_name(rep), value);
  820. #endif /* RELEASE */
  821.             if  (value > best) {
  822.                 best_rep = rep;
  823.                 best = value;
  824.                 best_dirbuf = *dirbuf;
  825.                }
  826.             }    /* if best so far */             
  827.          } /* if match */  
  828.             
  829.         } /* end while directory entries left to read */
  830.         closedir(dp);
  831.         
  832.         if (best_rep) {
  833.         format = best_rep;
  834.         base[-1] = '/';        /* Restore directory name */
  835.         base[0] = 0;
  836.         StrAllocCat(localname, best_dirbuf.d_name);
  837.         goto open_file;
  838.         
  839.         } else {             /* If not found suitable file */
  840.         free(localname);
  841.         return HTLoadError(sink, 403,    /* List formats? */
  842.            "Could not find suitable representation for transmission.");
  843.         }
  844.         /*NOTREACHED*/
  845.     } /* if multi suffix */
  846. /*
  847. **    Check to see if the 'localname' is in fact a directory.  If it is
  848. **    create a new hypertext object containing a list of files and 
  849. **    subdirectories contained in the directory.  All of these are links
  850. **      to the directories or files listed.
  851. **      NB This assumes the existance of a type 'STRUCT_DIRENT', which will
  852. **      hold the directory entry, and a type 'DIR' which is used to point to
  853. **      the current directory being read.
  854. */
  855.     
  856.     
  857.     if (stat(localname,&dir_info) == -1) {     /* get file information */
  858.                                    /* if can't read file information */
  859. #ifndef RELEASE
  860.         if (TRACE) fprintf(stderr, "HTFile: can't stat %s\n", localname);
  861. #endif /* RELEASE */
  862.  
  863.     }  else {        /* Stat was OK */
  864.         
  865.  
  866.         if (((dir_info.st_mode) & S_IFMT) == S_IFDIR) {
  867.         /* if localname is a directory */    
  868.  
  869.         HTStructured* target;        /* HTML object */
  870.         HTStructuredClass targetClass;
  871.  
  872.         DIR *dp;
  873.         STRUCT_DIRENT * dirbuf;
  874.         
  875.         char * logical;
  876.         char * tail;
  877.         
  878.         BOOL present[HTML_A_ATTRIBUTES];
  879.         
  880.         char * tmpfilename = NULL;
  881.         struct stat file_info;
  882. #ifndef RELEASE
  883.         if (TRACE)
  884.             fprintf(stderr,"%s is a directory\n",localname);
  885. #endif /* RELEASE */
  886.             
  887. /*    Check directory access.
  888. **    Selective access means only those directories containing a
  889. **    marker file can be browsed
  890. */
  891.         if (HTDirAccess == HT_DIR_FORBID) {
  892.             free(localname);
  893.             return HTLoadError(sink, 403,
  894.             "Directory browsing is not allowed.");
  895.         }
  896.  
  897.  
  898.         if (HTDirAccess == HT_DIR_SELECTIVE) {
  899.             char * enable_file_name = 
  900.             malloc(strlen(localname)+ 1 +
  901.              strlen(HT_DIR_ENABLE_FILE) + 1);
  902.             strcpy(enable_file_name, localname);
  903.             strcat(enable_file_name, "/");
  904.             strcat(enable_file_name, HT_DIR_ENABLE_FILE);
  905.             if (stat(enable_file_name, &file_info) != 0) {
  906.             free(localname);
  907.             return HTLoadError(sink, 403,
  908.             "Selective access is not enabled for this directory");
  909.             }
  910.         }
  911.  
  912.  
  913.         dp = opendir(localname);
  914.         if (!dp) {
  915.             free(localname);
  916.             return HTLoadError(sink, 403, "This directory is not readable.");
  917.         }
  918.  
  919.  
  920.  /*    Directory access is allowed and possible
  921.  */
  922.         logical = HTAnchor_address((HTAnchor*)anchor);
  923.         tail = strrchr(logical, '/') +1;    /* last part or "" */
  924.         
  925.         target = HTML_new(anchor, format_out, sink);
  926.         targetClass = *target->isa;    /* Copy routine entry points */
  927.             
  928.           { int i;
  929.             for(i=0; i<HTML_A_ATTRIBUTES; i++)
  930.                 present[i] = (i==HTML_A_HREF);
  931.         }
  932.         
  933.                 HTDirTitles(target, (HTAnchor *)anchor);
  934.  
  935.         if (HTDirReadme == HT_DIR_README_TOP)
  936.             do_readme(target, localname);
  937.         {
  938.             HTBTree * bt = HTBTree_new((HTComparer)strcasecomp);
  939.  
  940.             while ((dirbuf = readdir(dp))!=0)
  941.             {
  942.                 HTBTElement * dirname = NULL;
  943.  
  944.                 /* while there are directory entries to be read */
  945.                 if (dirbuf->d_ino == 0)
  946.                   /* if the entry is not being used, skip it */
  947.                 continue;
  948.             
  949.  
  950.                 /* if the current entry is parent directory */
  951.             if ((*(dirbuf->d_name)=='.') ||
  952.                 (*(dirbuf->d_name)==','))
  953.                 continue;    /* skip those files whose name begins
  954.                         with '.' or ',' */
  955.  
  956.             dirname = (HTBTElement *)malloc(
  957.                     strlen(dirbuf->d_name) + 2);
  958.             if (dirname == NULL) outofmem(__FILE__,"DirRead");
  959.             StrAllocCopy(tmpfilename,localname);
  960.             if (strcmp(localname,"/"))
  961.  
  962.                     /* if filename is not root directory */
  963.                 StrAllocCat(tmpfilename,"/"); 
  964.  
  965.  
  966.             StrAllocCat(tmpfilename,dirbuf->d_name);
  967.             stat(tmpfilename, &file_info);
  968.             if (((file_info.st_mode) & S_IFMT) == S_IFDIR)
  969.                 sprintf((char *)dirname,"D%s",dirbuf->d_name);
  970.             else sprintf((char *)dirname,"F%s",dirbuf->d_name);
  971.                 /* D & F to have first directories, then files */
  972.             HTBTree_add(bt,dirname); /* Sort dirname in the tree bt */
  973.             }
  974.  
  975.             /*    Run through tree printing out in order
  976.              */
  977.             {
  978.                 HTBTElement * next_element = HTBTree_next(bt,NULL);
  979.                 /* pick up the first element of the list */
  980.             char state;
  981.                 /* I for initial (.. file),
  982.                    D for directory file,
  983.                    F for file */
  984.             
  985.             state = 'I';
  986.  
  987.             while (next_element != NULL)
  988.                 {
  989.                 StrAllocCopy(tmpfilename,localname);
  990.                 if (strcmp(localname,"/")) 
  991.  
  992.                     /* if filename is not root directory */
  993.                     StrAllocCat(tmpfilename,"/"); 
  994.  
  995.                 StrAllocCat(tmpfilename,
  996.                     (char *)HTBTree_object(next_element)+1);
  997.                 /* append the current entry's filename to the path */
  998.                 HTSimplify(tmpfilename);
  999.                 /* Output the directory entry */
  1000.                 if (strcmp((char *)
  1001.                          (HTBTree_object(next_element)),"D.."))
  1002.                 {                
  1003.                 if (state != *(char *)(HTBTree_object(next_element)))
  1004.                 {
  1005.                     if (state == 'D')
  1006.                         END(HTML_DIR);
  1007.                     state = *(char *)
  1008.                         (HTBTree_object(next_element))=='D'?'D':'F';
  1009. #ifdef MSDOSMEM
  1010.     {
  1011.         extern void *vp_msdosmem;
  1012.         extern void **vpp_msdosmem;
  1013.         vp_msdosmem = NULL;
  1014.         vpp_msdosmem = NULL;
  1015.     }
  1016. #endif /* MSDOSMEM */
  1017.                     START(HTML_H2);
  1018.                     PUTS(state == 'D'?"Subdirectories:":"Files");
  1019.                     END(HTML_H2);
  1020. #ifdef MSDOSMEM
  1021.     {
  1022.         extern void *vp_msdosmem;
  1023.         extern void **vpp_msdosmem;
  1024.         vp_msdosmem = NULL;
  1025.         vpp_msdosmem = NULL;
  1026.     }
  1027. #endif /* MSDOSMEM */
  1028.                     START(HTML_DIR);
  1029.                 }
  1030. #ifdef MSDOSMEM
  1031.     {
  1032.         extern void *vp_msdosmem;
  1033.         extern void **vpp_msdosmem;
  1034.         vp_msdosmem = NULL;
  1035.         vpp_msdosmem = NULL;
  1036.     }
  1037. #endif /* MSDOSMEM */
  1038.                 START(HTML_LI);
  1039.                 }
  1040.                 HTDirEntry(target, tail,
  1041.                        (char*)HTBTree_object(next_element) +1);
  1042.                 PUTS(entry);
  1043.                 END(HTML_A);
  1044.  
  1045.                 next_element = HTBTree_next(bt,next_element);
  1046.                     /* pick up the next element of the list; 
  1047.                  if none, return NULL*/
  1048.             }
  1049.             if (state == 'I')
  1050.             {
  1051. #ifdef MSDOSMEM
  1052.     {
  1053.         extern void *vp_msdosmem;
  1054.         extern void **vpp_msdosmem;
  1055.         vp_msdosmem = NULL;
  1056.         vpp_msdosmem = NULL;
  1057.     }
  1058. #endif /* MSDOSMEM */
  1059.                 START(HTML_P);
  1060.                 PUTS("Empty Directory");
  1061.             }
  1062.             else
  1063.                 END(HTML_DIR);
  1064.             }
  1065.  
  1066.                 /* end while directory entries left to read */
  1067.             closedir(dp);
  1068.             free(logical);
  1069.             free(tmpfilename);
  1070.             HTBTreeAndObject_free(bt);
  1071.  
  1072.             if (HTDirReadme == HT_DIR_README_BOTTOM)
  1073.               do_readme(target, localname);
  1074.             FREE_TARGET;
  1075.             free(localname);
  1076.             return HT_LOADED;    /* document loaded */
  1077.         }
  1078.  
  1079.         } /* end if localname is directory */
  1080.     
  1081.     } /* end if file stat worked */
  1082.     
  1083. /* End of directory reading section
  1084. */
  1085. #endif
  1086. open_file:
  1087.     {
  1088.         FILE * fp = fopen(localname,"r");
  1089. #ifndef RELEASE
  1090.         if(TRACE) fprintf (stderr, "HTFile: Opening `%s' gives %p\n",
  1091.                 localname, (void*)fp);
  1092. #endif /* RELEASE */
  1093.         if (fp) {        /* Good! */
  1094.         if (HTEditable(localname)) {
  1095.             HTAtom * put = HTAtom_for("PUT");
  1096.             HTList * methods = HTAnchor_methods(anchor);
  1097.             if (HTList_indexOf(methods, put) == (-1)) {
  1098.             HTList_addObject(methods, put);
  1099.             }
  1100.         }
  1101.         free(localname);
  1102.         HTParseFile(format, format_out, anchor, fp, sink);
  1103.         fclose(fp);
  1104.         return HT_LOADED;
  1105.         }  /* If succesfull open */
  1106.     }    /* scope of fp */
  1107.     }  /* local unix file system */    
  1108. #endif
  1109. #endif
  1110.  
  1111. #ifndef DECNET
  1112. /*    Now, as transparently mounted access has failed, we try FTP.
  1113. */
  1114.     {
  1115.     if (strcmp(nodename, HTHostName())!=0)
  1116.         return HTFTPLoad(addr, anchor, format_out, sink);
  1117.     }
  1118. #endif
  1119.  
  1120. /*    All attempts have failed.
  1121. */
  1122.     {
  1123. #ifndef RELEASE
  1124.     if (TRACE)
  1125.         printf("Can't open `%s', errno=%d\n", addr, errno);
  1126. #endif /* RELEASE */
  1127.  
  1128.     return HTLoadError(sink, 403, "Can't access requested file.");
  1129.     }
  1130.     
  1131.  
  1132. }
  1133.  
  1134. /*        Protocol descriptors
  1135. */
  1136. GLOBALDEF PUBLIC HTProtocol HTFTP  = { "ftp", HTLoadFile, 0 };
  1137. GLOBALDEF PUBLIC HTProtocol HTFile = { "file", HTLoadFile, HTFileSaveStream };